﻿using System;
using System.Threading;
using System.Threading.Tasks;
using MugenMvvmToolkit.Interfaces;
using Windows.System.Threading;
using Windows.UI.Core;

namespace MugenMvvmToolkit.Infrastructure
{
    /// <summary>
    ///     Represents the default implemenation of <see cref="IThreadManager" />.
    /// </summary>
    public class ThreadManager : IThreadManager
    {
        #region Fields

        private CoreDispatcher _uiDispatcher;

        #endregion

        #region Constructor

        /// <summary>
        ///     Initializes a new instance of the <see cref="ThreadManager" /> class.
        /// </summary>
        public ThreadManager(CoreDispatcher uiDispatcher, TaskScheduler uiTaskSheduler,
                                    TaskScheduler nonUiTaskSheduler)
        {
            Should.NotBeNull(uiDispatcher, "uiDispatcher");
            Should.NotBeNull(uiTaskSheduler, "uiTaskSheduler");
            Should.NotBeNull(nonUiTaskSheduler, "nonUiTaskSheduler");

            UiDispatcher = uiDispatcher;
            UiTaskScheduler = uiTaskSheduler;
            NonUiTaskScheduler = nonUiTaskSheduler;
            AsynchronousPriority = CoreDispatcherPriority.Normal;
            SynchronousPriority = CoreDispatcherPriority.High;
        }

        #endregion

        #region Implementation of IThreadManager

        /// <summary>
        ///     Gets or sets a dispatcher associated with current UI thread.
        /// </summary>
        public CoreDispatcher UiDispatcher
        {
            get
            {
                Should.PropertyBeNotNull(_uiDispatcher, "UiDispatcher");
                return _uiDispatcher;
            }
            set
            {
                Should.PropertyBeNotNull(value, "UiDispatcher");
                _uiDispatcher = value;
            }
        }

        /// <summary>
        /// Gets or sets the priority of asynchronous operations.
        /// </summary>
        public CoreDispatcherPriority AsynchronousPriority { get; set; }

        /// <summary>
        /// Gets or sets the priority of synchronous operations.
        /// </summary>
        public CoreDispatcherPriority SynchronousPriority { get; set; }

        /// <summary>
        ///     Invokes an action on the UI thread using the UiDispatcher.
        /// </summary>
        /// <param name="action">
        ///     The specified <see cref="Action" />.
        /// </param>
        /// <param name="priority">The specified <see cref="CoreDispatcherPriority"/> to invoke the action.</param>
        public virtual void InvokeInUi(Action action, CoreDispatcherPriority priority)
        {
            if (UiDispatcher == null || UiDispatcher.HasThreadAccess)
            {
                action();
                return;
            }
            UiDispatcher.RunAsync(priority, () => action());
        }

        /// <summary>
        ///     Invokes an action on the UI thread synchronous using the UiDispatcher.
        /// </summary>
        /// <param name="action">
        ///     The specified <see cref="Action" />.
        /// </param>
        /// <param name="priority">The specified <see cref="CoreDispatcherPriority"/> to invoke the action.</param>
        public virtual void InvokeInUiSynchronous(Action action, CoreDispatcherPriority priority)
        {
            if (UiDispatcher == null || UiDispatcher.HasThreadAccess)
            {
                action();
                return;
            }
            using (EventWaitHandle wait = new AutoResetEvent(false))
            {
                UiDispatcher.RunAsync(priority, () =>
                {
                    action();
                    wait.Set();
                });
                wait.WaitOne();
            }
        }

        /// <summary>
        /// Determines whether the calling thread is the UI thread.
        /// </summary>        
        /// <returns><c>true</c> if the calling thread is the UI thread; otherwise, <c>false</c>.</returns>
        public bool IsUiThread
        {
            get
            {
                return UiDispatcher.HasThreadAccess;
            }
        }

        /// <summary>
        ///     Invokes an action on the UI thread.
        /// </summary>
        /// <param name="action">
        ///     The specified <see cref="Action" />.
        /// </param>
        public void InvokeInUi(Action action)
        {
            InvokeInUi(action, AsynchronousPriority);
        }

        /// <summary>
        ///     Invokes an action on the UI thread synchronous.
        /// </summary>
        /// <param name="action">
        ///     The specified <see cref="Action" />.
        /// </param>
        public void InvokeInUiSynchronous(Action action)
        {
            InvokeInUiSynchronous(action, SynchronousPriority);
        }

        /// <summary>
        ///     Invokes an action asynchronous.
        /// </summary>
        /// <param name="action">
        ///     The specified <see cref="Action" />.
        /// </param>
        /// <param name="state">The specified state.</param>
        public virtual void InvokeAsync(Action<object> action, object state)
        {
            ThreadPool.RunAsync(operation => action(state));
        }

        /// <summary>
        ///     Gets or sets a <see cref="TaskScheduler" /> associated with current UI thread.
        /// </summary>
        public TaskScheduler UiTaskScheduler { get; set; }

        /// <summary>
        ///     Gets or sets a <see cref="TaskScheduler" /> not associated with current UI thread.
        /// </summary>
        public TaskScheduler NonUiTaskScheduler { get; set; }

        #endregion
    }
}